/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.ainlolcat.aincalc2;

import org.ainlolcat.Variable;
import org.ainlolcat.ainscience.column.Column;
import org.ainlolcat.ainscience.tools;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Администратор
 */
public class LeksemmasHandler {
    Cleks vchold;
    public LeksemmasHandler(Cleks in){
        vchold = in;
    }

    //находит с заданной позиции лексемы разделители и их границу
    //в качестве идеи просто поиск скобочек и их четность и начало команд
    public int[] find_pos(List<Leksemma> leksems, int start_pos)        {
        boolean find = false;
        int[] pos = new int[2];

        // find start position
        for (int i = start_pos; i < leksems.size(); i++)
        {
            Leksemma current = leksems.get(i);
            if (
                    (
                            current.getType().equals(Leksemma.TYPE_COMMAND)     ||
                            current.getType().equals(Leksemma.TYPE_OUT_COMMAND) ||
                            current.getType().equals(Leksemma.TYPE_LBRACKET)
                    )
                    && (!find)
                )
            {
                pos[0] = i;
                find = true;
                break;
            }
        }

        // find close position
        if (find)
        {

            int dif = 0;
            if (leksems.get(pos[0]).getType().equals(Leksemma.TYPE_COMMAND)     ||
                    leksems.get(pos[0]).getType().equals(Leksemma.TYPE_OUT_COMMAND)     )
            {
                for (int i = pos[0] + 1; i < leksems.size(); i++)
                {
                    Leksemma current = leksems.get(i);
                    if (current.getType().equals(Leksemma.TYPE_LBRACKET)) dif++;
                    if (current.getType().equals(Leksemma.TYPE_RBRACKET)) dif--;
                    if (dif == 0) { pos[1] = i; return pos; }
                }
            }
            else
            {
                for (int i = pos[0]; i < leksems.size(); i++)
                {
                    Leksemma current = leksems.get(i);
                    if (current.getType().equals(Leksemma.TYPE_LBRACKET)) dif++;
                    if (current.getType().equals(Leksemma.TYPE_RBRACKET)) dif--;
                    if (dif == 0) { pos[1] = i; return pos; }
                }
            }
        }
        return null; // сисема прошла проверку на четность и не должна добраться до этого места, иначе ошибка в синтаксисе ф-й
        //
    }

    //вырезает из набора лексем кусок от start до end
    public List<Leksemma> cut(List<Leksemma> leksems, int start, int end)        {
        List<Leksemma> res = new ArrayList();
        for (int i = start; i <= end; i++)
        {
            res.add(leksems.get(i));
        }
        return res;
    }

    static String SQRT_COMMAND = "sqrt";
    static String ROUND_COMMAND = "round";
    static String COS_COMMAND = "cos";
    static String SIN_COMMAND = "sin";
    static String TAN_COMMAND = "tan";
    static String ABS_COMMAND = "abs";
    static String POW_COMMAND = "pow";
    static String VAL_COMMAND = "val";
    static String MAX_COMMAND = "max";
    static String MIN_COMMAND = "min";

    // отечает за передачу набора лексем корректному обработчику. является промежуточным звеном
    public Leksemma sendto(List<Leksemma> leksems) throws UnknownCommandException, WrongParametersCount {
        Leksemma start_lek = leksems.get(0);
        Integer type = start_lek.getType();
        if (type.equals(Leksemma.TYPE_LBRACKET))                         return base_act(leksems);
        if (type.equals(Leksemma.TYPE_COMMAND)){
            String command = (String) start_lek.getValue();
            if (command.compareTo(SQRT_COMMAND) == 0)                         return sqrt(leksems);
            if (command.compareTo(ROUND_COMMAND) == 0)                        return round(leksems);
            if (command.compareTo(COS_COMMAND) == 0)                          return cos(leksems);
            if (command.compareTo(SIN_COMMAND) == 0)                          return sin(leksems);
            if (command.compareTo(TAN_COMMAND) == 0)                          return tan(leksems);
            if (command.compareTo(ABS_COMMAND) == 0)                          return abs(leksems);
            if (command.compareTo(POW_COMMAND) == 0)                          return pow(leksems);
            if (command.compareTo(VAL_COMMAND) == 0)                          return val(leksems);
            // sumremum
            if (command.compareTo(MAX_COMMAND) == 0)                          return sort(leksems, 's');
            //infinum
            if (command.compareTo(MIN_COMMAND) == 0)                          return sort(leksems, 'i');
        }
        if (type.equals(Leksemma.TYPE_OUT_COMMAND)){
            String command = (String) start_lek.getValue();
           return vchold.getOutCommand(command).perform(leksems);
        }
        throw new UnknownCommandException(start_lek.getValue());
    }

    // первичный обработчик.
    public Leksemma start(List<Leksemma> leksems) throws UnknownCommandException, WrongParametersCount {
        if ( leksems != null && leksems.size()!=0)
        {
            //replace all var leksemmas by it's value
            for (int i=0; i < leksems.size(); i++){
                Leksemma lek = leksems.get(i);
                if (Leksemma.TYPE_VARIABLE.equals(lek.getType())){
                    lek = new Leksemma(
                            vchold.getVarValue(lek.getValue().toString()),
                            vchold.getVarType(lek.getValue().toString())
                    );
                    leksems.set(i,lek);
                }
            }
            //for (int i = 0;i<leksems.getColumnCount(); i++) Console.WriteLine(leksems.getData(i).toString());
            int[] buf = { 0, 0 };
            while (true)
            {
                buf = find_pos(leksems, buf[1]);
                //System.out.println(leksems);
                //System.out.println(buf);
                if (buf != null)
                {
                    Leksemma res = sendto(cut(leksems, buf[0], buf[1]));
                    for (int i = buf[0]; i <= buf[1]; i++)
                    {
                        leksems.remove(buf[0]);
                    }
                    leksems.add(buf[0], res);
                    buf[1] = buf[0];
                }
                else break;
            }
            return base_act(leksems);
        }
        else
        {
            throw new NullPointerException();
        }
    }

    //может обрабатывать элементарные действия, остальное переправляет через sendto другим обработчикам
    public Leksemma base_act(List<Leksemma> leksems) throws UnknownCommandException, WrongParametersCount {
        // удаляем скобочки, если они есть
        if (leksems.get(0).getType().equals(Leksemma.TYPE_LBRACKET)){
            leksems.remove(leksems.size() - 1);
            leksems.remove(0);
        }
        int[] buf = { 0, 0 };
        while (true)
        {
            buf = find_pos(leksems, buf[1]);
            if (buf != null)
            {
                Leksemma res = sendto(cut(leksems, buf[0], buf[1]));
                for (int i = buf[0]; i <= buf[1]; i++)
                {
                    leksems.remove(buf[0]);
                }
                leksems.add(buf[0], res);
                buf[1] = buf[0];
            }
            else break;
        }
        // perform *,/ ; +,-
        for (int i = 0; i < leksems.size(); i++)
        {
            if (leksems.get(i).getType().equals(Leksemma.TYPE_OPERAND)){
                String operand = leksems.get(i).getValue().toString();
                if (operand.compareTo("*") == 0) {
                    Leksemma lek1 = leksems.get(i - 1);
                    Leksemma lek2 = leksems.get(i + 1);
                    if (lek1.getType().equals(Leksemma.TYPE_INTEGER)){
                        if (lek2.getType().equals(Leksemma.TYPE_INTEGER)){
                            Integer l1 = (Integer) lek1.getValue();
                            Integer l2 = (Integer) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1*l2,Leksemma.TYPE_INTEGER));
                            i--;
                        } else if (lek2.getType().equals(Leksemma.TYPE_DOUBLE)){
                            Integer l1 = (Integer) lek1.getValue();
                            Double l2 = (Double) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1*l2,Leksemma.TYPE_DOUBLE));
                            i--;
                        }
                    } else if (lek1.getType().equals(Leksemma.TYPE_DOUBLE)){
                        if (lek2.getType().equals(Leksemma.TYPE_INTEGER)){
                            Double l1 = (Double) lek1.getValue();
                            Integer l2 = (Integer) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1*l2,Leksemma.TYPE_DOUBLE));
                            i--;
                        } else if (lek2.getType().equals(Leksemma.TYPE_DOUBLE)){
                            Double l1 = (Double) lek1.getValue();
                            Double l2 = (Double) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1*l2,Leksemma.TYPE_DOUBLE));
                            i--;
                        }
                    }
                }
                if (operand.compareTo("/") == 0)
                {
                    Leksemma lek1 = leksems.get(i - 1);
                    Leksemma lek2 = leksems.get(i + 1);
                    if (lek1.getType().equals(Leksemma.TYPE_INTEGER)){
                        if (lek2.getType().equals(Leksemma.TYPE_INTEGER)){
                            Integer l1 = (Integer) lek1.getValue();
                            Integer l2 = (Integer) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(new Double(l1/l2),Leksemma.TYPE_DOUBLE));
                            i--;
                        } else if (lek2.getType().equals(Leksemma.TYPE_DOUBLE)){
                            Integer l1 = (Integer) lek1.getValue();
                            Double l2 = (Double) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(new Double(l1/l2),Leksemma.TYPE_DOUBLE));
                            i--;
                        }
                    } else if (lek1.getType().equals(Leksemma.TYPE_DOUBLE)){
                        if (lek2.getType().equals(Leksemma.TYPE_INTEGER)){
                            Double l1 = (Double) lek1.getValue();
                            Integer l2 = (Integer) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(new Double(l1/l2),Leksemma.TYPE_DOUBLE));
                            i--;
                        } else if (lek2.getType().equals(Leksemma.TYPE_DOUBLE)){
                            Double l1 = (Double) lek1.getValue();
                            Double l2 = (Double) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(new Double(l1/l2),Leksemma.TYPE_DOUBLE));
                            i--;
                        }
                    }
                }
            }
        }
        for (int i = 0; i < leksems.size(); i++)
        {
            if (leksems.get(i).getType().equals(Leksemma.TYPE_OPERAND)){
                String operand = (String) leksems.get(i).getValue().toString();
                if (operand.compareTo("+") == 0) {
                    Leksemma lek1 = leksems.get(i - 1);
                    Leksemma lek2 = leksems.get(i + 1);
                    if (lek1.getType().equals(Leksemma.TYPE_INTEGER)){
                        if (lek2.getType().equals(Leksemma.TYPE_INTEGER)){
                            Integer l1 = (Integer) lek1.getValue();
                            Integer l2 = (Integer) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1+l2,Leksemma.TYPE_INTEGER));
                            i--;
                        } else if (lek2.getType().equals(Leksemma.TYPE_DOUBLE)){
                            Integer l1 = (Integer) lek1.getValue();
                            Double l2 = (Double) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1+l2,Leksemma.TYPE_DOUBLE));
                            i--;
                        }
                    } else if (lek1.getType().equals(Leksemma.TYPE_DOUBLE)){
                        if (lek2.getType().equals(Leksemma.TYPE_INTEGER)){
                            Double l1 = (Double) lek1.getValue();
                            Integer l2 = (Integer) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1+l2,Leksemma.TYPE_DOUBLE));
                            i--;
                        } else if (lek2.getType().equals(Leksemma.TYPE_DOUBLE)){
                            Double l1 = (Double) lek1.getValue();
                            Double l2 = (Double) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1+l2,Leksemma.TYPE_DOUBLE));
                            i--;
                        }
                    }
                }
                if (operand.compareTo("-") == 0)
                {
                    Leksemma lek1 = leksems.get(i - 1);
                    Leksemma lek2 = leksems.get(i + 1);
                    if (lek1.getType().equals(Leksemma.TYPE_INTEGER)){
                        if (lek2.getType().equals(Leksemma.TYPE_INTEGER)){
                            Integer l1 = (Integer) lek1.getValue();
                            Integer l2 = (Integer) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1-l2,Leksemma.TYPE_DOUBLE));
                            i--;
                        } else if (lek2.getType().equals(Leksemma.TYPE_DOUBLE)){
                            Integer l1 = (Integer) lek1.getValue();
                            Double l2 = (Double) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1-l2,Leksemma.TYPE_DOUBLE));
                            i--;
                        }
                    } else if (lek1.getType().equals(Leksemma.TYPE_DOUBLE)){
                        if (lek2.getType().equals(Leksemma.TYPE_INTEGER)){
                            Double l1 = (Double) lek1.getValue();
                            Integer l2 = (Integer) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1-l2,Leksemma.TYPE_DOUBLE));
                            i--;
                        } else if (lek2.getType().equals(Leksemma.TYPE_DOUBLE)){
                            Double l1 = (Double) lek1.getValue();
                            Double l2 = (Double) lek2.getValue();
                            leksems.remove(i - 1); leksems.remove(i - 1); leksems.remove(i - 1);
                            leksems.add(i - 1, new Leksemma(l1-l2,Leksemma.TYPE_DOUBLE));
                            i--;
                        }
                    }
                }
            }
        }
        //after all must remain only one
        return leksems.get(0);
    }

    public Leksemma val(List<Leksemma> leksems){
        if (leksems.size()==5){
            String linkname = (String) leksems.get(2).getValue();
            Variable value = vchold.entity.getLink(linkname).getValue(
                    (Integer) leksems.get(3).getValue(),     //columnID
                    (Integer) vchold.getVarValue("num")      //rowNum
            );
            return new Leksemma(value.getValue(),value.getType());
        }
        else{
            Leksemma columnID = leksems.get(2);
            Column column = null;
            if (columnID.getType() == Leksemma.TYPE_STRING){
                column = vchold.entity.getColumnByName((String) columnID.getValue());
            } else {
                column = vchold.entity.getColumnByOrder((Integer) columnID.getValue());
            }
            if (column == null)
                throw new MissingDependenciesException(vchold.entity.getName(), (String) columnID.getValue());
            int row = (Integer) vchold.getVarValue("num");
            Variable value = column.getCanonicalValue(row);
            return new Leksemma(value.getValue(),value.getType());
        }
    }

    public Leksemma sqrt(List<Leksemma> leksems) throws WrongParametersCount, UnknownCommandException {
        List<Leksemma> res = prepare_param_list(cut(leksems, 2, leksems.size() - 2));
        if (res.size() != 1){
            throw new WrongParametersCount();
        }
        double x;
        String sx = res.get(0).getValue().toString().replace(",", ".");
        //if (Double.TryParse(sx, out x))
        x=Double.parseDouble(sx);
        return new Leksemma(new Double(Math.sqrt(x)),Leksemma.TYPE_DOUBLE);
    }

    public Leksemma sin(List<Leksemma> leksems) throws WrongParametersCount, UnknownCommandException {
        List<Leksemma> res = prepare_param_list(cut(leksems, 2, leksems.size() - 2));
        if (res.size() != 1){
            throw new WrongParametersCount();
        }
        double x;
        String sx = res.get(0).getValue().toString();
        x = tools.parseDouble(sx);
        return new Leksemma(new Double(Math.sin(x)),Leksemma.TYPE_DOUBLE);
    }

    public Leksemma cos(List<Leksemma> leksems) throws WrongParametersCount, UnknownCommandException {
        List<Leksemma> res = prepare_param_list(cut(leksems, 2, leksems.size() - 2));
        if (res.size() != 1){
            throw new WrongParametersCount();
        }
        double x;
        String sx = res.get(0).getValue().toString();
        x = tools.parseDouble(sx);
        return new Leksemma(new Double(Math.cos(x)),Leksemma.TYPE_DOUBLE);
    }

    public Leksemma tan(List<Leksemma> leksems) throws WrongParametersCount, UnknownCommandException {
        List<Leksemma> res = prepare_param_list(cut(leksems, 2, leksems.size() - 2));
        if (res.size() != 1){
            throw new WrongParametersCount();
        }
        double x;
        String sx = res.get(0).getValue().toString();
        x = tools.parseDouble(sx);
        return new Leksemma(new Double(Math.tan(x)),Leksemma.TYPE_DOUBLE);
    }


    public Leksemma abs(List<Leksemma> leksems) throws WrongParametersCount, UnknownCommandException {
        List<Leksemma> res = prepare_param_list(cut(leksems, 2, leksems.size() - 2));
        if (res.size() != 1){
            throw new WrongParametersCount();
        }
        double x;
        String sx = res.get(0).getValue().toString();
        x = tools.parseDouble(sx);
        return new Leksemma(new Double(Math.abs(x)),Leksemma.TYPE_DOUBLE);
    }

    public Leksemma round(List<Leksemma> leksems) throws WrongParametersCount, UnknownCommandException {
        List<Leksemma> res = prepare_param_list(cut(leksems, 2, leksems.size() - 2));
        if (res.size() != 1){
            throw new WrongParametersCount();
        }
        double x;
        String sx = res.get(0).getValue().toString();
        x = tools.parseDouble(sx);
        return new Leksemma(new Double(Math.round(x)),Leksemma.TYPE_INTEGER);
    }

    public Leksemma sort(List<Leksemma> leksems, char mode) throws WrongParametersCount, UnknownCommandException {
        List<Leksemma> tosorting = prepare_param_list(cut(leksems, 2, leksems.size() - 2));
        if (tosorting.size() == 0){
            throw new WrongParametersCount();
        }
        else {
            double[] to_sort = new double[tosorting.size()];
            for (int i = 0; i < to_sort.length; i++) {
                to_sort[i] = Double.parseDouble(tosorting.get(i).getValue().toString());
            }

            // sort array
            for (int i = 0; i < to_sort.length; i++)
                for (int j = i + 1; j < to_sort.length; j++) {
                    if (to_sort[j] < to_sort[i]) {
                        double buffer = to_sort[i];
                        to_sort[i] = to_sort[j];
                        to_sort[j] = buffer;
                    }
                }
            Double ans = mode == 's'?to_sort[to_sort.length - 1]:to_sort[0];
            return new Leksemma(ans,Leksemma.TYPE_DOUBLE);
        }
    }
    public Leksemma pow(List<Leksemma> leksems) throws WrongParametersCount, UnknownCommandException {
        List<Leksemma> topow = prepare_param_list(cut(leksems,2,leksems.size()-2));
        if (topow.size() != 2)
        {
            throw new WrongParametersCount();
        }

        double x,y;
        String sx = topow.get(0).getValue().toString().replace(",", ".");
        String sy = topow.get(1).getValue().toString().replace(",", ".");
        x = Double.parseDouble(sx);
        y = Double.parseDouble(sy);
        return new Leksemma(new Double(Math.pow(x, y)),Leksemma.TYPE_DOUBLE);
    }

    public List<Leksemma> prepare_param_list(List<Leksemma> leksems) throws UnknownCommandException, WrongParametersCount {
        // после этих действий максимум, что останется это элементарные действия, так как все вложенные действия или функции отсеятся
        // clear all alien parameter (perform it )
        int[] buf = { 2, 2 };
        while (true)
        {
            buf = find_pos(leksems, buf[1]);
            if (buf != null)
            {
                Leksemma res = sendto(cut(leksems, buf[0], buf[1]));
                for (int i = buf[0]; i <= buf[1]; i++)
                {
                    leksems.remove(buf[0]);

                }
                leksems.add(buf[0], res);
                buf[1] = buf[0];
            }
            else break;
        }

        int startpos = 0;
        List<Leksemma> output = new ArrayList();
        for (int i = 0; i < leksems.size(); i++)
        {
            if ((leksems.get(i).toString().compareTo(",") == 0)||(i+1== leksems.size()))
            {
                Leksemma result = base_act(cut(leksems, startpos, i));
                if (result != null)
                {
                    output.add(result);
                    startpos = i + 1;
                }
                else
                {
                    return null;
                }
            }
        }
        return output;
    }
}
